package cn.jtfadmin.base.filesystem.config.local;


import cn.jtfadmin.base.filesystem.config.BaseFileSystemImpl;
import cn.jtfadmin.base.filesystem.config.FileResourceConfig;
import cn.jtfadmin.base.filesystem.config.FileResourceConfigDTO;
import cn.jtfadmin.base.filesystem.config.FileSystem;
import cn.jtfadmin.base.filesystem.config.local.FileDirConfigDTO;
import cn.jtfadmin.base.lang.common.utils.IdGenUtils;
import cn.jtfadmin.base.lang.common.utils.RequestUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 本地文件配置
 *
 * @author jtf
 * @since 0.0.1
 */

public class LocalFileSystemImpl extends BaseFileSystemImpl implements FileSystem, WebMvcConfigurer {
    /**
     * 文件配置参数
     */
    protected FileDirConfigDTO fileConfigDTO;

    private List<FileResourceConfigDTO> fileResourceConfigDTOList;


    public LocalFileSystemImpl(FileDirConfigDTO fileConfigDTO, ObjectProvider<FileResourceConfig> fileResourceConfigListOptional) {
        super(fileResourceConfigListOptional);
        this.fileConfigDTO = fileConfigDTO;
    }


    /**
     * 上传文件
     *
     * @param is   输入流
     * @param type 文件类型
     * @param ext  文件后缀
     * @return 文件名称
     */
    @Override
    public String upload(InputStream is, String type, String ext) {
        Path tempFile = new File(SystemUtils.getJavaIoTmpDir(), IdGenUtils.md5Gen() + "." + ext).toPath();
        try (InputStream inputStream = is){
            Files.copy(inputStream, tempFile);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        try (InputStream temInput = Files.newInputStream(tempFile)){
            String md5 = DigestUtils.md5Hex(temInput).toLowerCase();
            String name = md5 + "." + ext;
            String fileBaseDir = getFileBaseDir();
            if (Files.notExists(Paths.get(fileBaseDir, type, name))) {
                Path path = Paths.get(fileBaseDir, type);
                if (Files.notExists(path)) {
                    Files.createDirectories(path);
                }
                Files.copy(tempFile, Paths.get(fileBaseDir, type, name));
            }
            return "/"+ type + "/" + name;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public String upload( String type, InputStream is, String fileName) {

        try (InputStream inputStream = is){
            String fileBaseDir = getFileBaseDir();
            if (Files.notExists(Paths.get(fileBaseDir, type))) {
                Path path = Paths.get(fileBaseDir, type);
                if (Files.notExists(path)) {
                    Files.createDirectories(path);
                }
            }
            Path path = Paths.get(fileBaseDir, type, fileName);
            Files.deleteIfExists(path);
            Files.copy(inputStream, path);
            return "/"+ type + "/" + fileName;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    public String upload(String path, InputStream is) {
        try (InputStream inputStream = is){
            String fileBaseDir = getFileBaseDir();

            Path p = Paths.get(fileBaseDir, path);
            Path parent = p.getParent();
            if(Files.notExists(parent)){
             Files.createDirectories(parent);
            }
            Files.copy(inputStream, p);
            return path;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    @Override
    protected void registerFileResourceConfig(FileResourceConfig fileResourceConfig) {
        if (this.fileResourceConfigDTOList == null) {
            this.fileResourceConfigDTOList = new ArrayList<>();
        }
        FileResourceConfigDTO[] fileResourceConfigDTO = fileResourceConfig.getFileResourceConfigDTO();
        for (FileResourceConfigDTO resourceConfigDTO : fileResourceConfigDTO) {
            List<FileResourceConfigDTO> repeate = validateRepeate(resourceConfigDTO, this.fileResourceConfigDTOList);
            this.fileResourceConfigDTOList.add(resourceConfigDTO);
            this.fileResourceConfigDTOList.removeAll(repeate);
        }
    }

    /**
     * 获取所有的文件配置
     *
     * @return 所有的文件配置
     */
    @Override
    public List<FileResourceConfigDTO> getAllFileConfig() {
        return Collections.unmodifiableList(this.fileResourceConfigDTOList);
    }


    /**
     * 获取classPath 路径
     *
     * @return classPath路径
     */
    private String getClassPathDir() {
        String basedir = Thread.currentThread().getContextClassLoader().getResource("").getFile();
        if (basedir.contains("!")) {
            basedir = basedir.substring(0, basedir.indexOf("!"));
            basedir = basedir.replace("file:", "");

        }
        basedir = new File(basedir).getParentFile().getAbsolutePath();
        return basedir;
    }

    /**
     * 获取文件的基础路径<br/>
     * baseDir:如果配置有值，则用配置的值，如果配置没有值，则使用classpath 的值<br/>
     * fileDir:如果配置有值，则使用配置的值，如果没有配置，怎使用‘localFile’的值<br/>
     * 返回baseDir + fileDir
     *
     * @return 文件的基础路径
     */
    public String getFileBaseDir() {
        String basedir = fileConfigDTO.getBaseDir();
        if (!StringUtils.hasText(basedir)) {
            basedir = getClassPathDir();
            try {
                basedir = URLDecoder.decode(basedir, StandardCharsets.UTF_8.displayName());
            } catch (UnsupportedEncodingException e) {

            }
        }
        String dir = fileConfigDTO.getDir();
        if (!StringUtils.hasText(dir)) {
            dir = "localFile";
        }
        return Paths.get(basedir, dir).toString();
    }

    public String getFileTypeDir(String type) {
        return Paths.get(getFileBaseDir(), type).toString();
    }


    // 静态资源处理
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        Path path = Paths.get(getFileBaseDir());
        if (Files.notExists(path)) {
            try {
                Files.createDirectories(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        registry.addResourceHandler(fileConfigDTO.getAccessPrefix() + "/**")
                .addResourceLocations(path.toUri().toString());
    }


    /**
     * 获取文件访问的url
     *
     * @param fileRelativeUrl
     * @return
     */
    public String getFileVisitHttpUrl(String fileRelativeUrl) {
        if(!StringUtils.hasText(fileRelativeUrl)){
            return fileRelativeUrl;
        }
        if (fileRelativeUrl.startsWith("http") || fileRelativeUrl.startsWith("HTTP")) {
            return fileRelativeUrl;
        }
        String serverUrlPrefix = fileConfigDTO.getServerUrlPrefix();
        if (StringUtils.hasText(serverUrlPrefix)) {
            return megerUrl(serverUrlPrefix, fileRelativeUrl);
        }


        HttpServletRequest request = RequestUtils.getRequest();
        if (Objects.isNull(request)) {
            return fileRelativeUrl;
        }
        String requestBasePath = RequestUtils.getRequestBasePath(request);
        String accessBase = fileConfigDTO.getAccessPrefix();

        return megerUrl(megerUrl(requestBasePath, accessBase), fileRelativeUrl);

    }

    /**
     * 获取文件输入
     *
     * @param fileRelativeUrl
     * @return
     */
    @Override
    public InputStream getFileInputStream(String fileRelativeUrl) {
        Path path = Paths.get(getFileBaseDir(), fileRelativeUrl);
        if (Files.exists(path) && !Files.isDirectory(path)) {
            try {
                return Files.newInputStream(path);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return null;

    }


    private String megerUrl(String u1, String u2) {
        if (!StringUtils.hasText(u1)) {
            u1 = "";
        }
        if (!StringUtils.hasText(u2)) {
            u2 = "";
        }
        u1 = u1.trim();
        u2 = u2.trim();
        while (u1.endsWith("/")) {
            u1 = u1.substring(0, u1.length() - 1);
        }
        while (u2.startsWith("/")) {
            u2 = u2.substring(1);
        }

        return u1 + "/" + u2;
    }

    /**
     * 获取文件长度
     *
     * @param fileRelativeUrl
     * @return
     */
    public long getFileLength(String fileRelativeUrl) {
        Path path = Paths.get(getFileBaseDir(), fileRelativeUrl);
        if (Files.exists(path) && !Files.isDirectory(path)) {
            try {
                return Files.size(Paths.get(getFileBaseDir(), fileRelativeUrl));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return -1L;
    }

    @Override
    public String getFileName(String fileRelativeUrl) {
        Path path = Paths.get(getFileBaseDir(), fileRelativeUrl);
        if (Files.exists(path) && !Files.isDirectory(path)) {
           return path.toFile().getName();
        }
        return "";
    }
}
